package opensky.dbcache.dbvp.core;

import opensky.dbcache.dbvp.api.FullTableName;
import opensky.dbcache.dbvp.api.MapType;
import opensky.dbcache.dbvp.api.PartionRule;
import opensky.dbcache.dbvp.rule.MatchType;
import opensky.dbcache.dbvp.rule.Param;
import opensky.dbcache.dbvp.rule.RegExp;
import opensky.dbcache.dbvp.rule.Rule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static opensky.dbcache.dbvp.rule.RegExp.match;

/**
 * 规则匹配实现类
 *
 * @Author zhenggm
 * @Date 2017/9/21 下午4:04
 */
public class PartionRuleImpl implements PartionRule {

    private static Logger logger = LoggerFactory.getLogger(PartionRuleImpl.class);

    private Rule rule;


    public PartionRuleImpl(Rule r) {
        this.rule = r;
    }

    @Override
    public MapType getSchemaMapType() {
        return rule.getSchemaType();
    }

    @Override
    public String getSchema() {
        return rule.getSchema();
    }

    @Override
    public MapType getTableMapType() {
        return rule.getTableType();
    }

    @Override
    public String getTable() {
        return rule.getTable();
    }

    @Override
    public boolean map(FullTableName table) {
        boolean s = false;
        boolean t = false;
        if (rule.getSchemaType().equals(MapType.EQUAL)) {
            // schema是精确匹配
            s = table.getSchema().equals(rule.getSchema());// 直接比较是否相等
            if (rule.getTableType().equals(MapType.EQUAL)) {
                // table是精确匹配
                t = table.getTable().equals(rule.getTable());
            } else {
                // table是非精确匹配
                // 判断传入table是否匹配规则中的正则表达式
                t = this.regxStr(table.getTable(), rule.getTable());
            }
        } else {
            // schema是非精确匹配，正则
            s = this.regxStr(table.getSchema(), rule.getSchema());
            if (rule.getTableType().equals(MapType.EQUAL)) {
                t = table.getTable().equals(rule.getTable());
            } else {
                // 判断传入schema是否匹配规则中的正则表达式
                t = this.regxStr(table.getTable(), rule.getTable());
            }
        }
        // 只有当s和t都匹配上的时候
        if (s == true && t == true) {
            return true;
        }
        return false;
    }

    @Override
    public boolean isPassOn() {
        return rule.isProcess();
    }

    /**
     * 更换table和schema方法
     *
     * @param table   待转换的表
     * @param context 上下文传入参数
     * @return
     * @throws ParseException
     */
    @Override
    public FullTableName change(FullTableName table, Map<String, Object> context) throws ParseException {

        Boolean flag = false;
        // 遍历规则中的param列表，取出key匹配上下文中的参数，都匹配上则执行转换，匹配不上则返回null。
        if (!context.isEmpty() && context != null) {
            if (!rule.getMap().isEmpty() && rule.getMap() != null) {
                for (Map.Entry<String, Param> entry : rule.getMap().entrySet()) {
                    if (context.containsKey(entry.getKey())) {
                        flag = this.matchParam(entry.getKey(), entry.getValue(), context);
                        // flag为false，rule中的属性不匹配，也就是该规则不适合，直接返回null。
                        if (!flag) {
                            return null;
                        }
                    } else {
                        return null; // KEY匹配不上则返回null
                    }
                }
            } else {
                flag = true;
            }
        } else {
            if (rule.getMap().isEmpty() && rule.getMap() == null) {
                flag = true;
            }
        }

        // todo 验证是否成功
        if (flag) {
            // 替换${this}为当前schema和table
            String s = rule.getFullTableName().getSchema();
            String t = rule.getFullTableName().getTable();
            List<String> schemalist = RegExp.getKeywords(s);
            List<String> tablelist = RegExp.getKeywords(t);
            if (schemalist.size() > 0) {
                for (String n : schemalist) {
                    if (n.equals("schema")) {
                        s = s.replace("${schema}", table.getSchema());
                    }
                }
            }
            if (tablelist.size() > 0) {
                for (String n : tablelist) {
                    if (n.equals("table")) {
                        t = t.replace("${table}", table.getTable());
                    }
                }
            }
            // 替换{v1}中的所有v。
            if (!context.isEmpty() && context != null) {
                for (Map.Entry<String, Object> entry : context.entrySet()) {
                    s = s.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue()));
                    t = t.replace("{" + entry.getKey() + "}", String.valueOf(entry.getValue()));
                }
            }

            // 返回替换之后的结果。
            return new FullTableName(s, t);
        }
        return null;
    }

    private String replace(String s, String s1, String s2) {
        return s.replace(s1, s2);
    }

    // 默认属性匹配方法（equals,isNull,less,greater）
    private boolean matchParam(String key, Param param, Map<String, Object> context) throws ParseException {
        Boolean flag = false;
        try {
            // 如果param的匹配规则为isNull
            if (param.getType().equals(MatchType.ISNULL)) {
                // 判断传入的contex中key所对应的值是否为null，包括map中不存在该key。
                if (context.get(key) == null) {
                    return true;
                } else {
                    // 如果不为null，则isNull条件匹配失败。
                    return false;
                }
            }

            // 其余条件
            String c = param.getCLASS();
            Object context_value = null;
            Object param_value = null;

            switch (c) {
                case "java.lang.String":
                    context_value = (String) context.get(key);
                    param_value = param.getVaule();
                    break;
                case "java.lang.Integer":
                    context_value = (Integer) context.get(key);
                    param_value = Integer.valueOf(param.getVaule());
                    break;
                case "java.lang.Long":
                    context_value = (Long) context.get(key);
                    param_value = Long.valueOf(param.getVaule());
                    break;
                case "java.lang.Boolean":
                    context_value = (Boolean) context.get(key);
                    param_value = Boolean.valueOf(param.getVaule());
                    break;
                case "java.lang.Byte":
                    context_value = (Byte) context.get(key);
                    param_value = param.getVaule().getBytes();
                    break;
                case "java.lang.Float":
                    context_value = (Float) context.get(key);
                    param_value = Float.valueOf(param.getVaule());
                    break;
                case "java.lang.Double":
                    context_value = (Double) context.get(key);
                    param_value = Double.valueOf(param.getVaule());
                    break;
                case "java.lang.Character":
                    context_value = (Character) context.get(key);
                    param_value = param.getVaule().toCharArray();
                    break;
                //todo 验证日期格式
                case "java.util.Date":
                    SimpleDateFormat sdf = new SimpleDateFormat(param.getFormat());
                    context_value = (Date) context.get(key);
                    param_value = sdf.parse(param.getVaule());
                    break;
            }
            // todo 验证是否通过
            switch (param.getType()) {
                case EQUALS:
                    flag = context_value.equals(param_value);
                    break;
                case LESS:
                    if (c.equals("java.util.Date")) {
                        // 小于规则中的日期时
                        Date d1 = (Date) context_value;
                        Date d2 = (Date) param_value;
                        if (d1.getTime() < d2.getTime()) {
                            flag = true;
                        }
                        break;
                    }
                    if (c.equals("java.lang.Integer")) {
                        flag = (Integer) context_value < (Integer) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Long")) {
                        flag = (Long) context_value < (Long) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Float")) {
                        flag = (Float) context_value < (Float) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Double")) {
                        flag = (Double) context_value < (Double) param_value;
                        break;
                    }
                case GREATER:
                    if (c.equals("java.util.Date")) {
                        // 传入参数大于规则中的日期时
                        Date d3 = (Date) context_value;
                        Date d4 = (Date) param_value;
                        if (d3.getTime() > d4.getTime()) {
                            flag = true;
                        }
                        break;
                    }

                    if (c.equals("java.lang.Integer")) {
                        flag = (Integer) context_value > (Integer) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Long")) {
                        flag = (Long) context_value > (Long) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Float")) {
                        flag = (Float) context_value > (Float) param_value;
                        break;
                    }
                    if (c.equals("java.lang.Double")) {
                        flag = (Double) context_value > (Double) param_value;
                        break;
                    }
            }

        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        return flag;
    }


    // 正则匹配`${}`占位符
    // todo delete
    public List<String> getKeywords(String p) {
        String reg = "(?<=(?<!\\\\)\\$\\{)(.*?)(?=(?<!\\\\)\\})";
        RegExp re = new RegExp();
        List<String> list = re.find(reg, p);
        return list;
    }

    // 正则验证
    private boolean regxStr(String str, String regx) {
        return match(regx, str);
    }


    public Rule getRule() {
        return rule;
    }

    public void setRule(Rule rule) {
        this.rule = rule;
    }
}
